//****************************************************
// Function:	Simple Logic Analyzer (firmware) ver.2
// Target:		ATMega8515-16
// Compiler:	ICCAVR trial ver. 6.29
// Quartz:		16MHz
// Author:		Arkadiusz Antoniak @ 2004,2005 Poland
//****************************************************
//
// Adapted for Atmega 324 by Joep Suijs (C) 2008
//

#include "avr/interrupt.h"
#include "avr/io.h"
#include "Delay.h"

//********************** MACROS ***********************
#define HEADER_VALUE 	   			   65
#define RECSTATE_WAITFORGREETING  	   1
#define RECSTATE_WAITFORQFRAME    	   2
#define RECSTATE_SENDINGSAMPLES    	   3
#define LED_RED						   PORTD&=~(1<<2)
#define LED_GREEN					   PORTD|=(1<<2)

#define MEMCOUNTERRST_SET			   PORTD|=(1<<7)
#define MEMCOUNTERRST_CLR			   PORTD&=~(1<<7)
#define MEMREADSTROBE_SET			   PORTB|=(1<<3)
#define MEMREADSTROBE_CLR			   PORTB&=~(1<<3)
#define MEMWRITESTROBE_SET			   PORTD|=(1<<5)
#define MEMWRITESTROBE_CLR			   PORTD&=~(1<<5)

#define LATCHOUT_HIZ				   PORTA|=(1<<7)
#define LATCHOUT_NORMAL				   PORTA&=~(1<<7)


//********************** HEADERS **********************
void CommAbort(void);

//********************** GLOBALS **********************
volatile unsigned char QueryFrame[6],RecState,CRC_All_Samples;
volatile unsigned char fQFrameComplete,fQFrameActive,fGreeting;
volatile unsigned char fSamplingEnd,fSampleBlockRequest;
volatile unsigned char T0_Counter,NumOfSamples256,SendingBlockNumber;

//********************** FUNCTIONS ********************
//******************************************
// Timer0 Overflow Interrupt
// executed when every 256 samples sampled
//******************************************
SIGNAL(TIMER0_OVF_vect)
{
 if(++T0_Counter>=NumOfSamples256)
 {
  TCCR1A=0x00;
  TCCR1B=0x00;		//Stop Timer1
  TCCR0B=0x00;		//Stop Timer0
  MEMWRITESTROBE_CLR;
  MEMREADSTROBE_SET;
  fSamplingEnd=1;
 }
}

//*******************************
// RS232 Byte Receive Interrupt
//*******************************
SIGNAL(SIG_USART_RECV)
{
 static unsigned char FrameCounter;
 unsigned char data;
 
 data=UDR0;
 
 //Quit sampling or data sending or any other activity
 if((data=='Q') && (fQFrameActive==0))
 {
    CommAbort();
	return;
 }
 
 //Allow for sending greeting to PC
 if(RecState==RECSTATE_WAITFORGREETING)
 {
    if(data=='I')
       fGreeting=1;
 	else
 	   fGreeting=0;

    return;
 }
 
 //Query frame receiving
 if(RecState==RECSTATE_WAITFORQFRAME)
 {
    if(fQFrameComplete==1)
 	   return;

    if((data==HEADER_VALUE) && (fQFrameActive==0))
 	{
       FrameCounter=0;
	   fQFrameActive=1;
 	}
 	else
 	{
  	   if(fQFrameActive==0)
	   	  return;
		
	   QueryFrame[FrameCounter++]=data;
	   if(FrameCounter>=6)
	   {
		  fQFrameComplete=1;
		  fQFrameActive=0;
		  FrameCounter=6;
	   }
    }
	return;
 }
 
 //Sending samples
 if(RecState==RECSTATE_SENDINGSAMPLES)
 {
    if((data<32) || (data=='C'))
	{
	    SendingBlockNumber=data;
		fSampleBlockRequest=1;
	}
	
	return;
 }
}

//*******************************
// RS232 Init
//*******************************
void Init_UART(void)
{
 UCSR0B|=(1<<TXEN0);		 		 //enable transmitter		 
 UCSR0B|=(1<<RXEN0)|(1<<RXCIE0);	 //enable receiver + interrupt		 
 UCSR0A|=(1<<U2X0); 		 		 //double speed
 UBRR0H=0;
// UBRR0L=51;				 		 //38400 baud @ 16 MHz, err=0.16%
 UBRR0L=64;				 		 //38400 baud @ 20 MHz

 UCSR0C=(3<<UCSZ00);	 //8bit, no parity, one stop bit
}

//*******************************
// RS232 Send one byte
//*******************************
void SerOut(unsigned char byte)
{
 UCSR0A|=(1<<TXC0);
 UDR0=byte;
 while((UCSR0A&(1<<TXC0))==0);
}

//*******************************
// RS232 Send string (unsigned)
//*******************************
void SendString(unsigned char* pString)
{
 unsigned char *a;

 for(a=pString;*a;a++)
 	SerOut(*a); 
}

//*******************************
// RS232 Send greeting string
//*******************************
void SendGreeting(void)
{
 SendString((unsigned char *) "SLA_ANTONIAK_V1\0");
}

//*******************************
// Abort any activity 
// SOFTWARE RESET
//*******************************
void CommAbort(void)
{
	//WDTimer On
	WDTCSR&=~(7<<WDP0);		//aprox. 16ms
	WDTCSR|=(1<<WDE);
	while(1);				//wait for Watchdog reset
}

//*******************************
// Setting sampling parameters
//*******************************
void SetSamplingParams(unsigned char freq, unsigned char samples)
{
   switch(freq)
   {
   		case 1:  //2MHz
			 	 OCR1A=0x03;
			     break;
   		case 2:  //200kHz
			 	 OCR1A=0x27;
			     break;
   		case 3:  //20kHz
			 	 OCR1A=0x018F;
			     break;
   		case 4:  //2kHz
			 	 OCR1A=0x0F9F;
			     break;
		default:
   			 OCR1AH=0x00;
   			 OCR1AL=0x03;
			     break;
   }
   
   switch(samples)
   {
   	    case 1:
                 NumOfSamples256=4; 	 //4*256=1024
		         break;
   	    case 2:
                 NumOfSamples256=8; 	 //8*256=2048
		         break;
   	    case 3:
                 NumOfSamples256=16; 	 //16*256=4096
		         break;
   	    case 4:
                 NumOfSamples256=32; 	 //32*256=8192
		         break;
   	    case 5:
                 NumOfSamples256=64; 	 //64*256=16384
		         break;
   	    case 6:
                 NumOfSamples256=128; 	 //128*256=32768
		         break;
		default:
                 NumOfSamples256=4; 	 //4*256=1024
		         break;
   }
   TIMSK0=(1<<TOIE0);
   LATCHOUT_NORMAL;
   MEMREADSTROBE_SET;
   MEMWRITESTROBE_CLR;
   MEMCOUNTERRST_SET;
   asm("nop");
   asm("nop");
   asm("nop");
   MEMCOUNTERRST_CLR;
   fSamplingEnd=0;
   T0_Counter=0;
   TCCR0B=0x07;
   TCNT0=0;
}

//*******************************
// Reading samples (1kB) from RAM
// and sending to PC
// SendingBlockNumber = 0...31
//*******************************
void SendSamples(void)
{
   unsigned int i,SamplesQty;
   unsigned char SampleByte;

   LATCHOUT_HIZ;
   
   MEMREADSTROBE_SET;
   MEMWRITESTROBE_CLR;
   
   MEMCOUNTERRST_SET;
   asm("nop");
   asm("nop");
   asm("nop");
   MEMCOUNTERRST_CLR;
   
   SamplesQty=1024*(unsigned int)(SendingBlockNumber);
   for(i=0;i<SamplesQty;i++)
   {
   	   MEMREADSTROBE_CLR;
   	   asm("nop");
   	   asm("nop");
   	   asm("nop");
   	   MEMREADSTROBE_SET;
   	   asm("nop");
   	   asm("nop");
   	   asm("nop");
   }

   for(i=0;i<1024;i++)
   {
   	   MEMREADSTROBE_CLR;
   	   asm("nop");
   	   asm("nop");
   	   asm("nop");
   	   MEMREADSTROBE_SET;
	   
	   SampleByte=PINC;
	   SerOut(SampleByte);
   }
   
   MEMREADSTROBE_SET;
   MEMWRITESTROBE_CLR;
   LATCHOUT_NORMAL;
}

//*******************************
// Send CRC of all samples
//*******************************
void SendCRC(void)
{
   SerOut(CRC_All_Samples);
   CommAbort();
}

//*******************************
// Count 8-bit CRC
// Polynomial: x^8+x^5+x^4+1
// (same as in Dallas' iButtons)
//*******************************
void CRC8_Dallas(unsigned char byte, unsigned char *CRC)
{
	unsigned char zp1,zp2,i;

	zp1=byte;
	for(i=0;i<8;i++)
	{
		byte^=*CRC;         
		zp2=byte&0x01;      
		byte=*CRC;
		if(zp2)
			byte^=0x18;

		byte=((byte)>>1)+0x80*zp2; 
                                  
		*CRC=byte;
		zp1=(byte=zp1>>1);    
	}
}

//*******************************
// Count CRC of all samples
//*******************************
void CRC_AllSamples(void)
{
   unsigned int i,SamplesQty;
   unsigned char SampleByte;

   LATCHOUT_HIZ;
   MEMCOUNTERRST_SET;
   asm("nop");
   asm("nop");
   asm("nop");
   MEMCOUNTERRST_CLR;
   
   SamplesQty=256*(unsigned int)(NumOfSamples256);
   
   CRC_All_Samples=0;
   for(i=0;i<SamplesQty;i++)
   {
   	   MEMREADSTROBE_CLR;
   	   asm("nop");
   	   asm("nop");
   	   asm("nop");
   	   MEMREADSTROBE_SET;
	   
	   SampleByte=PINC;
	   CRC8_Dallas(SampleByte,(unsigned char *) &CRC_All_Samples);
   }
   
   MEMREADSTROBE_SET;
   MEMWRITESTROBE_CLR;
   LATCHOUT_NORMAL;
}

//*******************************
// Triggering in AUTO EDGE mode
// code optimised for max. speed
// that's why it's so ugly :)
//*******************************
void TriggerEdge(unsigned char edge)
{
 unsigned char B_PIN;
 
 B_PIN=QueryFrame[1];
 if(edge==1)  			  //falling edge
 {
  	switch(B_PIN)
	{
	 	case 0:
	 		   	 while(!(PINC&(1<<0)));
				 while(PINC&(1<<0));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 1:
	 		   	 while(!(PINC&(1<<1)));
				 while(PINC&(1<<1));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 2:
	 		   	 while(!(PINC&(1<<2)));
				 while(PINC&(1<<2));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 3:
	 		   	 while(!(PINC&(1<<3)));
				 while(PINC&(1<<3));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 4:
	 		   	 while(!(PINC&(1<<4)));
				 while(PINC&(1<<4));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 5:
	 		   	 while(!(PINC&(1<<5)));
				 while(PINC&(1<<5));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 6:
	 		   	 while(!(PINC&(1<<6)));
				 while(PINC&(1<<6));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 7:
	 		   	 while(!(PINC&(1<<7)));
				 while(PINC&(1<<7));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	}
 }
 else			 //rising edge
 {
  	switch(B_PIN)
	{
	 	case 0:
				 while(PINC&(1<<0));
	 		   	 while(!(PINC&(1<<0)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 1:
				 while(PINC&(1<<1));
	 		   	 while(!(PINC&(1<<1)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 2:
				 while(PINC&(1<<2));
	 		   	 while(!(PINC&(1<<2)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 3:
				 while(PINC&(1<<3));
	 		   	 while(!(PINC&(1<<3)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 4:
				 while(PINC&(1<<4));
	 		   	 while(!(PINC&(1<<4)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 5:
				 while(PINC&(1<<5));
	 		   	 while(!(PINC&(1<<5)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 6:
				 while(PINC&(1<<6));
	 		   	 while(!(PINC&(1<<6)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	 	case 7:
				 while(PINC&(1<<7));
	 		   	 while(!(PINC&(1<<7)));
				 MEMWRITESTROBE_SET;
				 TCCR1A=0x48;
				 TCCR1B=0x09;	  //Start
			   	 break;
	}
 }
}

//********************** MAIN *************************
int main(void)
{
  unsigned char i,sum;
  unsigned int WaitForTimeout;
  unsigned char B_CMP;
 
 //WDTimer Off
 MCUSR &= ~ (1<<WDRF);
 WDTCSR=(1<<WDCE)|(1<<WDE);
 WDTCSR=0x00;
 
 DDRA=0xFF;
 DDRB=0xFE;
 DDRC=0x00;
 DDRD=0xBE;
 ACSR|=(1<<ACD);
 MCUCR&=0xFE;
 MCUCR|=0x20;	//Idle enable
   Init_UART();
 asm("sei");

 LED_GREEN;
 MEMREADSTROBE_SET;
 MEMWRITESTROBE_CLR;
 LATCHOUT_NORMAL;
 RecState=RECSTATE_WAITFORGREETING;
 fGreeting=0;
 fQFrameActive=0;
 fQFrameComplete=0;
 WaitForTimeout=100;

 asm("sleep");
 
 while(1)
 {
 	Waitms(10);
	
  	if(RecState==RECSTATE_WAITFORGREETING)
	{
	    //Sending greeting string to PC
	 	if(fGreeting==1)
		{
		    SendGreeting();
			fGreeting=0;
    		fQFrameComplete=0;
			fQFrameActive=0;
			WaitForTimeout=100;
			RecState=RECSTATE_WAITFORQFRAME;
		}

	 	if(--WaitForTimeout==0)
		 	CommAbort();

	 	continue;
	}

  	if(RecState==RECSTATE_WAITFORQFRAME)
	{
	 	if(fQFrameComplete==1)
		{
		 	fQFrameComplete=0;
		 	sum=0;
			for(i=0;i<5;i++)
			   sum+=QueryFrame[i];
		
			if(sum==QueryFrame[5])
			{
			   //Sampling
			   LED_RED;
			   SetSamplingParams(QueryFrame[3],QueryFrame[4]);

			   //Start sampling - MAN or AUTO
			   if(QueryFrame[0]==1)
			   {
			        //MAN
			   		TCCR1A=0x48;
			        TCCR1B=0x09;
			   }
			   else if(QueryFrame[0]==2)
			   {
			        //AUTO CMP
					B_CMP=QueryFrame[2];
					
					//Trigger
					while(PINC==B_CMP) ;
					while(PINC!=B_CMP) ;
						
					MEMWRITESTROBE_SET;
					TCCR1A=0x48;
					TCCR1B=0x09;			   //Start
			   }
			   else
			   {
			   		//AUTO EDGE
					TriggerEdge(QueryFrame[2]);
			   }

			   //Wait until sampling done
			   while(fSamplingEnd==0) ;

			   CRC_AllSamples();
			   
			   WaitForTimeout=3000;	   	 //30s over transmission time
			   SendingBlockNumber=0;   	 //1kB-long Block nr. 0
			   fSampleBlockRequest=0;
			   RecState=RECSTATE_SENDINGSAMPLES;

			   //Sending 1kB-long block number 0
			   SendSamples();			   
			   
			   continue;
			}
			else
			{
			   CommAbort();
			}
		}

	 	if(--WaitForTimeout==0)
		 	CommAbort();
			
	 	continue;
	}
	
	if(RecState==RECSTATE_SENDINGSAMPLES)
	{
	    if(fSampleBlockRequest!=0)
		{
		 	fSampleBlockRequest=0;
            if(SendingBlockNumber<32)
		 	    SendSamples();
				
			//'C' - Special command for CRC
			if(SendingBlockNumber=='C')
				SendCRC();
		}
		
		if(--WaitForTimeout==0)
		 	CommAbort();
	}	
 }
 
 return 0;
}
